//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file	psys_memory.h
///
///
///
//==================================================================

#ifndef PSYS_MEMORY_H
#define PSYS_MEMORY_H

#include <tchar.h>

//==================================================================
namespace PSYS {

//==================================================================
//== SWITCHABLE DEFINES
//==================================================================
#ifdef _DEBUG
	#define PSYS_MALLOC(_X_)			PSYS::Malloc_dbg((_X_),__FILE__,__LINE__)
	#define PSYS_MALLOC_EX(_X_,_Y_)		PSYS::MallocEx_dbg((_X_),(_Y_),__FILE__,__LINE__)
	#define PSYS_REALLOC(_X_,_Y_)		PSYS::Realloc_dbg((_X_),(_Y_),__FILE__,__LINE__)
	#define PSYS_REALLOC2(_X_,_Y_)		PSYS::Realloc2_dbg((_X_),(_Y_),__FILE__,__LINE__)
	#define PSYS_CMALLOC(_X_)			PSYS::Calloc_dbg((_X_),__FILE__,__LINE__)
	#define PSYS_FREE(_X_)				PSYS::Free_dbg((_X_),__FILE__,__LINE__)
#else
	#define PSYS_MALLOC					PSYS::Malloc
	#define PSYS_MALLOC_EX				PSYS::MallocEx
	#define PSYS_REALLOC				PSYS::Realloc
	#define PSYS_REALLOC2				PSYS::Realloc2
	#define PSYS_CMALLOC				PSYS::psys_cmalloc
	#define PSYS_FREE					PSYS::Free
#endif

//==================================================================
//== MEMORY FUNCTIONS
//==================================================================
typedef struct
{
	u_int	total_allocated;
	u_int	n_allocated_blocks;
} psys_mem_info_t;

void psys_mem_info_get( psys_mem_info_t *mip );

unsigned int psys_mem_allocated(void);
unsigned int psys_mem_max(void);

//------------------------------------------------------------------
/** Allocates a block of system memory.

	@param   size  		- Size of the memory block requested.
	
	@return   A pointer to the newly allocated block or NULL if the
			  block couldn't be allocated.
			  
	@see psys_cmalloc(), Free(), Realloc(), Realloc2().
*/
void *Malloc( u_int size );

void *Realloc( void *p, u_int size );
void *Realloc2( void **pp, u_int size );

//------------------------------------------------------------------
/** Allocates a clean (0 filled) block of system memory.

	@param   size  		- Size of the memory block requested.
	
	@return   A pointer to the newly allocated block or NULL if the
			  block couldn't be allocated.

	@see Malloc(), Free(), Realloc(), Realloc2().
*/
void *psys_cmalloc( u_int size );

//------------------------------------------------------------------
void _Free( void *p );

//------------------------------------------------------------------
/** Releases a block of system memory.

	@param   p  		- Pointer to the origin of the memory block.
	
	@see	Malloc(), psys_cmalloc(), Realloc(), Realloc2().
*/
void Free( void *p );

void *Malloc_dbg( u_int size, const char *filep, int line );
void *Realloc_dbg( void *p, u_int size, const char *filep, int line );
void *Realloc2_dbg( void **pp, u_int size, const char *filep, int line );
void *Calloc_dbg( u_int size, const char *filep, int line );
void _Free_dbg( void *p, const char *filep, int line );
void Free_dbg( void *p, const char *filep, int line );

//==================================================================
/* class for counted reference semantics
* - deletes the object to which it refers when the last sync_ptr
*   that refers to it is destroyed
*/
/*
template <class T>
class sync_ptr {
private:
	T		*ptr;     // pointer to the value
	int		_extern_revcnt;

public:
	// initialize pointer with existing pointer
	// - requires that the pointer p is a return value of new
	explicit sync_ptr (T* p=0) : ptr(p), _extern_revcnt(-1)
	{
	}

	// copy pointer (one more owner)
	sync_ptr (const sync_ptr<T>& p) throw() : ptr(p.ptr), _extern_revcnt(-1)
	{
	}

	// destructor (delete value if this was the last owner)
	~sync_ptr () throw()
	{
	}

	// assignment (unshare old and share new value)
	sync_ptr<T>& operator= (const sync_ptr<T>& p) throw()
	{
		ptr = p;
		_extern_revcnt = p._extern_revcnt;
		return *this;
	}

	// access the value to which the pointer refers
	T& operator*() const throw()
	{
		return *ptr;
	}
	T* operator->() const throw()
	{
		return ptr;
	}

	bool operator!() { return (ptr == NULL); }

	bool needs_update()
	{
		if ( _extern_revcnt != ptr->_revcnt )
		{
			_extern_revcnt = ptr->_revcnt;
			return true;
		}
		return false;
	}

	bool needs_update( int local_revcnt )
	{
		if ( _extern_revcnt != local_revcnt )
		{
			_extern_revcnt = local_revcnt;
			return true;
		}
		return false;
	}
};
*/

//==================================================================
template <class T>
class sync_ptr
{
public:
	sync_ptr() :
		_ptr(NULL),
		_extern_revcnt(-1)
	{
	}
	
	sync_ptr( T* p ) :
		_ptr(p),
		_extern_revcnt(-1)
	{
	}

	~sync_ptr()
	{
//		if ( _ptr )
//			delete _ptr;
	}

	sync_ptr &operator = (T*p)
	{
		_ptr = p;
		return *this;
	}

	T**  operator&()  { return &_ptr; }
	T*   operator->() { return _ptr; }
	operator T*(){ return _ptr; }
	bool operator!() { return (_ptr == NULL); }

	bool needs_update()
	{
		if ( _extern_revcnt != _ptr->_revcnt )
		{
			_extern_revcnt = _ptr->_revcnt;
			return true;
		}
		return false;
	}

	bool needs_update( int local_revcnt )
	{
		if ( _extern_revcnt != local_revcnt )
		{
			_extern_revcnt = local_revcnt;
			return true;
		}
		return false;
	}

private:
	sync_ptr(const sync_ptr &p);                  // unimplemented copy
	sync_ptr &operator = (const sync_ptr &p);     // unimplemented assign

protected:
	T		*_ptr;
	int		_extern_revcnt;
};


//==================================================================
///
//==================================================================
class sync_obj
{
public:
	enum LockType
	{
		LOCK_R	= 1,
		LOCK_W	= 2,
		LOCK_RW = LOCK_R | LOCK_W,
	};

private:
	const static int	MAX_LOCKS = 16;
	u_int		_lock_stack[MAX_LOCKS];
	
	const char	*_src_filep[MAX_LOCKS];
	int			_src_line[MAX_LOCKS];

	int			_cur_lock_idx;

public:
	int		_revcnt;	///< Revision counter. Increase at every change.

	sync_obj() :
		_cur_lock_idx(0),
		_revcnt(0)
	{
	}

	void	LockR( const char *filep, int line )
	{
		PASSERT( _cur_lock_idx < MAX_LOCKS );

		_src_filep[ _cur_lock_idx ] = filep;
		_src_line[ _cur_lock_idx ] = line;

		_lock_stack[_cur_lock_idx++] = LOCK_R;
	}
	void	LockW( const char *filep, int line )
	{
		PASSERT( _cur_lock_idx < MAX_LOCKS );

		_src_filep[ _cur_lock_idx ] = filep;
		_src_line[ _cur_lock_idx ] = line;

		_lock_stack[_cur_lock_idx++] = LOCK_W;
	}
	void	LockRW( const char *filep, int line )
	{
		PASSERT( _cur_lock_idx < MAX_LOCKS );

		_src_filep[ _cur_lock_idx ] = filep;
		_src_line[ _cur_lock_idx ] = line;

		_lock_stack[_cur_lock_idx++] = LOCK_R | LOCK_W;
	}
	void	Unlock()
	{
		PASSERT( _cur_lock_idx > 0 );
		if ( _lock_stack[--_cur_lock_idx] & 2 )
			_revcnt += 1;
	}

	void	AssertLockR() const
	{
		PASSERT( _cur_lock_idx > 0 );
		PASSERT( (_lock_stack[_cur_lock_idx-1] & LOCK_R) == LOCK_R );
	}

	void	AssertLockW() const
	{
		PASSERT( _cur_lock_idx > 0 );
		PASSERT( (_lock_stack[_cur_lock_idx-1] & LOCK_W) == LOCK_W );
	}
};

//==================================================================
class sync_item
{
	int		_local_revcnt;

public:
	sync_item() :
		_local_revcnt(-1)
	{
	}

	bool needs_update( int obj_revcnt )
	{
		if ( _local_revcnt != obj_revcnt )
		{
			_local_revcnt = obj_revcnt;
			return true;
		}
		return false;
	}
};

//==================================================================
};

//==================================================================
template <class T>
class com_ptr
{
public:
	com_ptr(){ _ptr=NULL; }
	com_ptr(T*p){ _ptr=p; }
	~com_ptr(){ if(_ptr) _ptr->Release();
				//PDEBUG_PRINTF( "Released %08X\n", _ptr );
				}
	com_ptr &operator = (T*p) { _ptr = p; return *this; }

	T**  operator&()  { return &_ptr; }
	T*   operator->() { return _ptr; }
	operator T*(){ return _ptr; }
	bool operator!() { return (_ptr == NULL); }

private:
	com_ptr(const com_ptr &p);                  // unimplemented copy
	com_ptr &operator = (const com_ptr &p);     // unimplemented assign

	T *_ptr;
};

//==================================================================
template <class T>
class autodel
{
public:
	autodel(){ _ptr=NULL; }
	autodel(T*p){ _ptr=p; }
	~autodel(){ if(_ptr) delete _ptr; }
	autodel &operator = (T*p) { _ptr = p; return *this; }

	T**  operator&()  { return &_ptr; }
	T*   operator->() { return _ptr; }
	operator T*(){ return _ptr; }
	bool operator!() { return (_ptr == NULL); }

	const T*	GetPtr() const	{ return _ptr; }
	T*			GetPtr()		{ return _ptr; }

private:
	autodel(const autodel &p);                  // unimplemented copy
	autodel &operator = (const autodel &p);     // unimplemented assign

	T *_ptr;
};

#endif